package com.googlecode.gwtresume.client.api;

import static com.googlecode.gwtresume.client.api.ResumeConstants.ADDRESS;
import static com.googlecode.gwtresume.client.api.ResumeConstants.DESCRIPTION;
import static com.googlecode.gwtresume.client.api.ResumeConstants.EMAIL;
import static com.googlecode.gwtresume.client.api.ResumeConstants.EMPLOYER;
import static com.googlecode.gwtresume.client.api.ResumeConstants.FIRST;
import static com.googlecode.gwtresume.client.api.ResumeConstants.FROM;
import static com.googlecode.gwtresume.client.api.ResumeConstants.HEADER;
import static com.googlecode.gwtresume.client.api.ResumeConstants.HREF;
import static com.googlecode.gwtresume.client.api.ResumeConstants.LAST;
import static com.googlecode.gwtresume.client.api.ResumeConstants.LEVEL;
import static com.googlecode.gwtresume.client.api.ResumeConstants.LINE;
import static com.googlecode.gwtresume.client.api.ResumeConstants.LOCATION;
import static com.googlecode.gwtresume.client.api.ResumeConstants.NAME;
import static com.googlecode.gwtresume.client.api.ResumeConstants.OLDER;
import static com.googlecode.gwtresume.client.api.ResumeConstants.POSITION;
import static com.googlecode.gwtresume.client.api.ResumeConstants.PRESENT;
import static com.googlecode.gwtresume.client.api.ResumeConstants.PROJECT;
import static com.googlecode.gwtresume.client.api.ResumeConstants.SKILL;
import static com.googlecode.gwtresume.client.api.ResumeConstants.SKILL_SET;
import static com.googlecode.gwtresume.client.api.ResumeConstants.TITLE;
import static com.googlecode.gwtresume.client.api.ResumeConstants.TO;
import static com.googlecode.gwtresume.client.api.ResumeConstants.VERSIONS;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.xml.client.Document;
import com.google.gwt.xml.client.Element;
import com.google.gwt.xml.client.Node;
import com.google.gwt.xml.client.NodeList;
import com.google.gwt.xml.client.Text;

public class XmlResume implements Resume {

	static final SkillLevel DEFAULT_SKILL_LEVEL = SkillLevel.strong;
	static final DateTimeFormat DF = DateTimeFormat.getFormat("MM/yyyy");
	
	final ResumeConstants constants;
	final Header header;
	final ArrayList<SkillSet> skillSets = new ArrayList<SkillSet>();
	final ArrayList<Position> experience = new ArrayList<Position>();
	final ArrayList<String[]> credits = new ArrayList<String[]>();
	final String summary;

	public XmlResume(ResumeConstants constants, Document dom) {
		super();
		this.constants = constants;
		this.header = new HeaderImpl((Element) dom.getElementsByTagName(HEADER).item(0));
        
		NodeList nl = dom.getElementsByTagName(SKILL_SET);
        for (int i = 0; i < nl.getLength(); i++) {
        	skillSets.add(new SkillSetImpl((Element) nl.item(i)));
        }
        
        for (Element e : getElementsByTagName(dom.getDocumentElement(), POSITION)) {
        	experience.add(new PositionImpl(e));
        }
        
		for (Element credit : getElementsByTagName(dom.getDocumentElement(), "credit")) {
			credits.add(getTextContent(credit.getElementsByTagName("line")));
		}
        
        this.summary = getTextContent(dom.getElementsByTagName("summary").item(0));
	}

	public ResumeConstants getConstants() {
		return constants;
	}

	public Header getHeader() {
		return header;
	}
	
	public String getSummary() {		
		return this.summary;
	}

	public List<SkillSet> getSkills() {
		return skillSets;
	}
	
	public ArrayList<Position> getExperience() {
		return experience;
	}

	public List<String[]> getEducation() {
		return credits;
	}

	public class HeaderImpl implements Header {

		final String[] address;
		final String email;
		final String firstName;
		final String lastName;
		final String phone;
		
		public HeaderImpl(Element header) {
			super();
			this.address = getTextContent(getElementByTagName(header, ADDRESS).getElementsByTagName(LINE));
			this.email = getTextContent(getElementByTagName(header, EMAIL));

			this.firstName = getTextContent(getElementByTagName(header, FIRST));
			this.lastName = getTextContent(getElementByTagName(header, LAST));
			this.phone = getTextContent(getElementByTagName(header, "phone"));
		}

		public String[] getAddress() {
			return address;
		}

		public String getEmail() {
			return email;
		}

		public String getFirstName() {
			return firstName;
		}
		
		public String getLastName() {
			return lastName;
		}
		
		public String getPhone() {
			return phone;
		}

	}

	public abstract class Titled {
		
		final String title;

		public Titled(Element e) {
			super();
			this.title = e.getAttribute(TITLE);
		}
		public String getTitle() {
			return title;
		}

		
	}
	
	public class SkillSetImpl extends Titled implements SkillSet {
		

		final List<Skill> skills = new ArrayList<Skill>();

		public SkillSetImpl(Element e) {
			super(e);
			NodeList nl = e.getElementsByTagName(SKILL);
	        for (int i = 0; i < nl.getLength(); i++) {
	        	skills.add(new SkillImpl((Element) nl.item(i)));
	        }				
		}

		public List<Skill> getSkills() {
			return skills;
		}


	}

	public class SkillImpl extends Titled implements Skill {
		
		final String name;
		final String url;
		final String versions;
		final boolean includesOlderVersions;
		final SkillLevel skillLevel;
		
		public SkillImpl(Element e) {
			super(e);
			this.name = getTextContent(e);
			this.url = e.getAttribute(HREF);
			this.versions = e.getAttribute(VERSIONS);
			this.includesOlderVersions = "true".equals(e.getAttribute(OLDER));
			String level = e.getAttribute(LEVEL);
			this.skillLevel = (level != null && level.trim().length() > 0) ? SkillLevel.valueOf(level) : DEFAULT_SKILL_LEVEL;

		}

		public String getName() {			
			return name;
		}

		public String getUrl() {
			return url;
		}

		public String getVersions() {
			return versions;
		}

		public boolean includesOlderVersions() {
			return includesOlderVersions;			
		}

		public SkillLevel getSkillLevel() {
			return skillLevel;
		}
		
		
		
	}
	
	public class PositionImpl extends Titled implements Position {
		
		final String employer;
		final ArrayList<Project> projects = new ArrayList<Project>();
		final String description;
		final Date endDate;
		final Date startDate;
		final String location;
		final String url;
		final String type;
		
		PositionImpl(Element e) {
			super(e);
			Element employer = getElementByTagName(e, EMPLOYER);
			this.employer = employer.getAttribute(NAME);
			this.location = e.getAttribute(LOCATION);
			for (Element pe : getElementsByTagName(e, PROJECT)) {
				projects.add(new ProjectImpl(pe));
			}
			
			String to = e.getAttribute(TO);
			this.endDate = to.equals(PRESENT) ? null : DF.parse(to);
			this.startDate = DF.parse(e.getAttribute(FROM));
			this.url = employer.getAttribute("href");
			this.type = e.getAttribute("type");
			this.description =  getTextContent(getElementByTagName(e, DESCRIPTION));
		}

		public String getDescription() {
			return description;
		}

		public String getEmployer() {
			return employer;
		}

		public Date getEndDate() {
			return endDate;
			
		}

		public boolean isPresentPosition() {
			return endDate == null;
		}
		
		public String getLocation() {
			return location;
		}

		public List<Project> getProjects() {
			return projects;
		}

		public Date getStartDate() {
			return startDate;
		}

		public String getType() {
			return type;
		}

		public String getUrl() {
			return url;
		}
		
		
	}
	
	public class ProjectImpl implements Project {
		
		final String description;
		final String descriptionExt;
		final String name;
		
		public ProjectImpl(Element e) {
			super();
			this.name = e.getAttribute("name");
			this.description = getTextContent(e);
			Element detail = getElementByTagName(e, "detail");
			this.descriptionExt = (detail != null ? getTextContent(detail) : null);
		}

		public String getDescription(boolean detailed) {
			StringBuffer desc = new StringBuffer(description);
			if (descriptionExt != null && detailed) {
				desc.append(" ");
				desc.append(descriptionExt);
			}
			return desc.toString();
		}

		public String getName() {
			return name;
		}
		
		
		
	}
	
	
	
	
	static Element getElementByTagName(Element e, String name) {
		return (Element) e.getElementsByTagName(name).item(0);
	}
	
	static Element[] getElementsByTagName(Element e, String name) {
		NodeList nl = e.getElementsByTagName(name);
		Element[] elements = new Element[nl.getLength()];
		for (int i = 0; i < nl.getLength(); i++) elements[i] = (Element) nl.item(i);
		return elements;
	}
	
	static String getTextContent(Node n) {
		try {

			StringBuffer sb = new StringBuffer();
			NodeList nl = n.getChildNodes();
			for (int i = 0; i < nl.getLength(); i++) {
				Node ni = nl.item(i);
				if (ni instanceof Element && !ni.getNodeName().equals("detail")) sb.append(ni.toString());
				else if (ni instanceof Text) sb.append(((Text) ni).getData());
			}
			String text = sb.toString().replaceAll("\\n", " ");
			return text.replaceAll("\\s\\s+|\\t", " ");
		} catch (Throwable x) {
			x.printStackTrace();
			return "error";
		}
	}

	static String[] getTextContent(NodeList nl) {
		try {
		String[] s = new String[nl.getLength()];
		for (int i = 0; i < nl.getLength(); i++) 
			s[i] = getTextContent(nl.item(i));
		return s;
		} catch (Throwable x) {
			x.printStackTrace();
			return new String[] {"error"};
		}
	}

}
